package com.authine.cloudpivot.app.openapi.config;

import com.alibaba.cloud.nacos.ribbon.NacosServerIntrospector;
import com.alibaba.cola.dto.RequestContext;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient;
import org.springframework.cloud.netflix.ribbon.RibbonUtils;
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

/**
 * @author carter
 * create_date  2020/11/4 10:56
 * description    重写ribbon的负载均衡客户端
 */
@Component
public class LocalDebugLoadBalancerClient extends RibbonLoadBalancerClient {

    private final SpringClientFactory clientFactory;


    public LocalDebugLoadBalancerClient(SpringClientFactory clientFactory) {
        super(clientFactory);
        this.clientFactory = clientFactory;
    }

    /**
     * 根据请求和url进行特定路由
     *
     * @param serviceId 服务名称
     * @param exchange  请求对象，可提取请求参数
     * @return 特定的负载均衡实例
     */
    public ServiceInstance choose(String serviceId, ServerWebExchange exchange) {

        Map<String, String> map = new HashMap<>(4);

        String loginId = Optional.ofNullable(exchange).map(ServerWebExchange::getRequest).map(req -> Optional.of(req)
                .map(ServerHttpRequest::getHeaders)
                .map(item -> item.getFirst(RequestContext.KeyDict.LOGIN_ID_KEY))
                .filter(StringUtils::isNotBlank)
                .orElse(Optional.of(req)
                        .map(ServerHttpRequest::getQueryParams)
                        .map(item -> item.getFirst(RequestContext.KeyDict.LOGIN_ID_KEY))
                        .orElse(""))).orElse("");

        String loginName = Optional.ofNullable(exchange).map(ServerWebExchange::getRequest).map(req -> Optional.of(req)
                .map(ServerHttpRequest::getHeaders)
                .map(item -> item.getFirst(RequestContext.KeyDict.LOGIN_NAME_KEY))
                .filter(StringUtils::isNotBlank)
                .orElse(Optional.of(req)
                        .map(ServerHttpRequest::getQueryParams)
                        .map(item -> item.getFirst(RequestContext.KeyDict.LOGIN_NAME_KEY))
                        .orElse(""))).orElse("");

        String requestId = Optional.ofNullable(exchange).map(ServerWebExchange::getRequest).map(req -> Optional.of(req)
                .map(ServerHttpRequest::getHeaders)
                .map(item -> item.getFirst(RequestContext.KeyDict.REQUEST_ID_KEY))
                .filter(StringUtils::isNotBlank)
                .orElse(Optional.of(req)
                        .map(ServerHttpRequest::getQueryParams)
                        .map(item -> item.getFirst(RequestContext.KeyDict.REQUEST_ID_KEY))
                        .orElse(""))).orElse("");

        String debugRequestId = Optional.ofNullable(exchange).map(ServerWebExchange::getRequest).map(req -> Optional.of(req)
                .map(ServerHttpRequest::getHeaders)
                .map(item -> item.getFirst(RequestContext.KeyDict.DEBUG_REQUEST_ID_KEY))
                .filter(StringUtils::isNotBlank)
                .orElse(Optional.of(req)
                        .map(ServerHttpRequest::getQueryParams)
                        .map(item -> item.getFirst(RequestContext.KeyDict.DEBUG_REQUEST_ID_KEY))
                        .orElse(""))).orElse("");
        map.put(RequestContext.KeyDict.LOGIN_ID_KEY, loginId);
        map.put(RequestContext.KeyDict.REQUEST_ID_KEY, requestId);
        map.put(RequestContext.KeyDict.DEBUG_REQUEST_ID_KEY, debugRequestId);
        map.put(RequestContext.KeyDict.LOGIN_NAME_KEY, loginName);
        map.put("serviceId", serviceId);

        ILoadBalancer loadBalancer = super.getLoadBalancer(serviceId);
        Server server = loadBalancer.chooseServer(map);
        //如果为空，则按照父类的负载均衡策略选择；
        return new RibbonLoadBalancerClient.RibbonServer(serviceId, server, isSecure(server, serviceId), serverIntrospector(serviceId).getMetadata(server));
    }

    private boolean isSecure(Server server, String serviceId) {
        IClientConfig config = clientFactory.getClientConfig(serviceId);
        ServerIntrospector serverIntrospector = serverIntrospector(serviceId);
        return RibbonUtils.isSecure(config, serverIntrospector, server);
    }

    private ServerIntrospector serverIntrospector(String serviceId) {
        ServerIntrospector serverIntrospector = clientFactory.getInstance(serviceId, ServerIntrospector.class);
        if (serverIntrospector == null) {
            serverIntrospector = new NacosServerIntrospector();
        }

        return serverIntrospector;
    }


}
